#!/bin/bash
configfile="<%= scope.lookupvar('puppi::params::basedir') %>/puppi.conf"

# This is the actual command used to run the different scripts
# Use cat for debugging noop purposes 
# runcommand="cat"
runcommand=""

# Define defaults
verbosity="max"
show="yes"

# Define action tag
export tag=$(date +%Y%m%d-%H%M%S)

counter=0

# Load general configurations
if [ ! -f $configfile ] ; then
    echo "Config file: $configfile not found"
    exit 1
else
    . $configfile
    . $scriptsdir/functions
fi

# Main functions
check_host () {
    for command in $(ls -1 $checksdir) ; do
        title="$HOSTNAME check: $command"
        code=$(cat "$checksdir/$command")
        ask_interactive
        output=$($runcommand "$checksdir/$command" 2>&1)
        handle_result
    done
}

check () {
    for command in $(ls -1 $projectsdir/$project/check) ; do
        title="$HOSTNAME check: $command"
        code=$(cat "$projectsdir/$project/check/$command")
        ask_interactive
        output=$($runcommand "$projectsdir/$project/check/$command" 2>&1)
        handle_result
    done

    check_host
    # show_report
}

log () {
    tailcommand="tail"
    which colortail >/dev/null 2>&1  && tailcommand="colortail"

    if [ "x$project" != "xdefault" ] ; then
        if [ $logsdir/$project ] ; then
            alllog="$alllog $(cat $logsdir/$project)"
        else
            echo "WARNING: $logsdir/$project does not exist!"
            exit 1
        fi
    else
        if [ "$interactive" = "yes" ] ; then
            echo "Choose one or more log topics to show. Select the last number (done) to end selection"
            PS3="Type one number to add a log topic to the show list."
            all_choices=""
            select choice in $( ls $logsdir ) done
            do
                echo "You selected $choice [$REPLY]"
                [[ $choice == "done" ]] && break
                all_choices="$all_choices $choice"
                echo "Your choices: $all_choices"
            done
            for log in $all_choices ; do
                alllog="$alllog $(cat $logsdir/$log)"
            done
        else
            for log in $(ls $logsdir) ; do
                alllog="$alllog $(cat $logsdir/$log)"
            done
        fi
    fi

    # So, show something
    if [ ! -z "$counts" ] ; then
        if [ ! -z "$greppattern" ] ; then
            $tailcommand -n $counts $alllog | grep $greppattern
        else
            $tailcommand -n $counts $alllog
        fi
    else
        if [ ! -z "$greppattern" ] ; then
            $tailcommand -f $alllog | grep $greppattern
        else
            $tailcommand -f $alllog
        fi
    fi
}

info () {
    if [ "x$project" != "xdefault" ] ; then
        if [ $infodir/$project ] ; then
            $infodir/$project 
        else
            echo "WARNING: $infodir/$project does not exist!"
            exit 1
        fi
    else
        if [ "$interactive" = "yes" ] ; then
            echo "Choose one or more topics to show. Select the last number (done) to end selection"
            PS3="Type one number to add an info topic to the show list."
            all_choices=""
            select choice in $( ls $infodir ) done
            do
                echo "You selected $choice [$REPLY]"
                [[ $choice == "done" ]] && break
                all_choices="$all_choices $choice"
                echo "Your choices: $all_choices"
            done
            for info in $all_choices ; do
                if [ ! -z "$greppattern" ] ; then
                    $infodir/$info | grep $greppattern
                else
                    $infodir/$info
                fi
            done
        else
            for info in $(ls $infodir) ; do
                if [ ! -z "$greppattern" ] ; then
                    $infodir/$info | grep $greppattern
                else
                    $infodir/$info
                fi
            done
        fi
    fi
}


todo () {
    for todo in $(ls $tododir) ; do
        $tododir/$todo
    done
}


rollback () {
    if [ ! -z $rollbackversion ] ; then
        save_runtime_config "rollbackversion=$rollbackversion" || initerr=1
    else
        echo "Choose deploy to rollback:"
        ls -1 $archivedir/$project
        read rollbackversion
        save_runtime_config "rollbackversion=$rollbackversion" || initerr=1
    fi
    
    for command in $(ls -1 $projectsdir/$project/rollback) ; do
        title="$HOSTNAME Rollback: $command"
        code=$(cat "$projectsdir/$project/rollback/$command")
        ask_interactive
        output=$($runcommand "$projectsdir/$project/rollback/$command" 2>&1)
        handle_result
    done

    send_reports
    show_report
    [ "$result" = "OK" ] && exit 0
}

deploy () {
    for command in $(ls -1 $projectsdir/$project/deploy) ; do
        title="$HOSTNAME Deploy: $command"
        code=$(cat "$projectsdir/$project/deploy/$command")
        ask_interactive
        output=$($runcommand "$projectsdir/$project/deploy/$command" 2>&1)
        handle_result
        [ "$EXITCRIT" = "1" ] && [ "$force" != "yes" ] && break
        if [ "$DONTDEPLOY" = "1" ] ; then
             echo "No need to deploy: source file has not changed"
             echo "Type 'rm $archivedir/$project/md5sum' and run puppi again to force deployment" 
             exit 0
        fi
    done

    send_reports
    show_report
    [ "$result" = "OK" ] && exit 0
}

initialize () {
    for command in $(ls -1 $projectsdir/$project/initialize) ; do
        title="$HOSTNAME Init: $command"
        code=$(cat "$projectsdir/$project/initialize/$command")
        ask_interactive
        output=$($runcommand "$projectsdir/$project/initialize/$command" 2>&1)
        handle_result
        [ "$EXITCRIT" = "1" ] && [ "$force" != "yes" ] && break
    done

    send_reports
    show_report
    [ "$result" = "OK" ] && exit 0
}

save_summary () {
    tagend=$(date +%Y%m%d-%H%M%S)

    result="OK"
    if [ "$EXITWARN" = "1" ] ; then
        result="WARNING"
    fi
    if [ "$EXITCRIT" = "1" ] ; then
        result="ERROR"
    fi

    echo "Report for $action on $project" > $logdir/$project/$tag/summary
    echo "Job start: $tag" >> $logdir/$project/$tag/summary
    echo "Job end: $tagend" >> $logdir/$project/$tag/summary
    echo "Job result: $result" >> $logdir/$project/$tag/summary
    echo "Actions executed:" >> $logdir/$project/$tag/summary
    cd $logdir/$project/$tag/
    for message in $(ls -1 $logdir/$project/$tag/ | grep -v summary ) ; do
        msg_title=$(head -1 $message)
        msg_code=$(head -2 $message | tail -1) 
        msg_result=$(head -3 $message | tail -1) 
        echo "[$msg_result] $msg_title : $msg_code" >> $logdir/$project/$tag/summary
    done

    # Copy runtime config in archive
    cp $workdir/$project/config $logdir/$project/$tag/

    # Write runtime config on Summary
    echo >> $logdir/$project/$tag/summary
    echo "RUNTIME CONFIGURATION" >> $logdir/$project/$tag/summary
    cat $workdir/$project/config | grep -vE "^#|^$"  >> $logdir/$project/$tag/summary

}

send_reports () {
    if [[ "x$report" == "xyes" ]] ; then
        save_summary
        for command in $(ls -1 $projectsdir/$project/report) ; do
            title="Reporting: $command"
            code=$(cat "$projectsdir/$project/report/$command")
            echo -n $title
            output=$($runcommand "$projectsdir/$project/report/$command" 2>&1)
            # handle_result # This breaks the overall exit code when deploy fails
        done
    fi
}

show_report () {
    echo
    echo "REPORT FOR PUPPI - STATUS $result"
    echo "Summary of operations is: $logdir/$project/$tag/summary "
    echo "Details are in: $logdir/$project/$tag/"
    echo "Temporary workdir has been: $workdir/$project/ (Will be rewritten at the next puppi run)"
    echo "Runtime config file is: $workdir/$project/config"
    echo "Files have been archived in: $archivedir/$project/$tag"
    test "$testmode" = "yes" && echo "This was a TEST RUN! Nothing has been done for real."
}


create_runtime_conf () {
    if [[ ( ! -e $projectsdir/$project ) && ( ! -e $infodir/$project ) && ( ! -e $logsdir/$project ) ]] ; then
        showhelp
        exit 1
    fi

    initerr=0

    # When project is unset we set it to default
    [ ! -z "$project" ] || export project="default"

    # Clean up and Create runtime configuration file 
#    command="00-$project-RuntimeConfig-Initialization"
#    title="Puppi setup: $command"
#    code="rm -rf $workdir/$project && touch $workdir/$project/config [...]"
#    echo -n $title

    echo $workdir | grep tmp >/dev/null 2>&1 || ( echo "Workdir must contain string tmp" ; exit 1 )
    rm -rf $workdir/$project || initerr=1

    mkdir -p $workdir/$project || initerr=1
    touch $workdir/$project/config || initerr=1

    test -r "$projectsdir/$project/config" && cp $projectsdir/$project/config $workdir/$project/
    chmod 644 $workdir/$project/config || initerr=1

    save_runtime_config "project=$project" || initerr=1
    save_runtime_config "tag=$tag" || initerr=1
    save_runtime_config "action=$action" || initerr=1

    storedir=$workdir/$project/store || initerr=1
    mkdir -p $storedir || initerr=1
    save_runtime_config "storedir=$storedir" || initerr=1

    predeploydir=$workdir/$project/deploy || initerr=1
    mkdir -p $predeploydir || initerr=1
    save_runtime_config "predeploydir=$predeploydir" || initerr=1

    save_runtime_config "force=$force" || initerr=1
    save_runtime_config "testmode=$testmode" || initerr=1
    save_runtime_config "interactive=$interactive" || initerr=1
    save_runtime_config "debug=$debug" || initerr=1
    save_runtime_config "report=$report" || initerr=1
    save_runtime_config "show=$show" || initerr=1
    save_runtime_config "counts=$counts" || initerr=1
    save_runtime_config "greppattern=$greppattern" || initerr=1

    for oopt in $(echo $options) ; do
        save_runtime_config "$(echo $oopt)" || initerr=1
    done
    
    echo $initerr | grep "0" 2>&1 > /dev/null
#    handle_result
}


showhelp () {
   echo "Usage: puppi <command> [project|topic] [options]"
   echo " "
   echo "Available commands:"
   echo "check [project] [-s <yes|no|fail>] - Run puppi checks host or project wide"
   echo "log [topic] [-i] [-g <pattern>] - Show system and application specific logs"
   echo "info [topic] [-i] [-g <pattern>] - Show informations about the system"
   echo "todo - Show todo's checklist of the system"
   echo "init <project> [-i] [-f] [-t] - First time project initialization and setup"
   echo "deploy <project> [-i] [-f] [-t] [-o ...] - Deploy the specified project"
   echo "rollback <project> [state] [-i] [-f] [-t] - Rollback the specified project. "
   echo " "
   echo "Available options:"
   echo "-f - Force puppi commands execution flow also on CRITICAL errors"
   echo "-i - Interactively ask confirmation for every step"
   echo "-t - Test mode. Just show the commands that should be executed"
   echo "-d <yes|full> - Debug mode. Show debug of what is done."
   echo "-r <yes|no|fail> - Enable reporting: yes/no/only on failures. Default depends on action"
   echo "-s <yes|no|fail> - Show output: yes/no/only for failures. Default: yes"
   echo "-g <pattern> - Grep command output with the selected pattern"
   echo "-o \"parameter=value parameter2=value2\" - Set manual options to override defaults"
   echo " "
   echo "Available deploy projects:"
   ls -1 $projectsdir
   echo 
   echo "Available info topics:"
   ls $infodir 
   echo 
   echo "Available log topics:"
   ls $logsdir
}

# Check Input
if [ "$#" = "0" ] ; then
    showhelp
    exit
fi

while [ $# -gt 0 ]; do
  case "$1" in
    deploy|init)
      report="yes"
      export action=$1
      if [ -n "$2" ] ; then
        echo "$2" | egrep -q "^-.$"
        if [ "$?" != "0" ] ; then
          export project=$(shell_filter_strict $2)
          shift 2
        else
          shift
        fi
      else
        showhelp
        exit
        shift
      fi
    ;;
    rollback)
      report="yes"
      export action=$1
      if [ -n "$3" ] ; then
        echo "$3" | egrep -q "^-.$"
        if [ "$?" != "0" ] ; then
          export project=$(shell_filter_strict $2)
          export rollbackversion=$(shell_filter_strict $3)
          shift 3
        else
          shift 2
        fi
      elif [ -n "$2" ] ; then
        echo "$2" | egrep -q "^-.$"
        if [ "$?" != "0" ] ; then
          export project=$(shell_filter_strict $2)
          shift 2
        else
          shift
        fi
      else
        showhelp
        exit
        shift
      fi
    ;;
    check)
      report="no"
      export action="checkhost"
      if [ -n "$2" ] ; then
        echo "$2" | egrep -q "^-.$"
        if [ "$?" != "0" ] ; then
          export project=$(shell_filter_strict $2)
          export action="check"
          shift 2
        else
          shift
        fi
      else
        shift
      fi
    ;;
    log)
      report="no"
      export action="log"
      if [ -n "$2" ] ; then
        echo "$2" | egrep -q "^-.$"
        if [ "$?" != "0" ] ; then
          export project=$(shell_filter_strict $2)
          shift 2
        else 
          shift
        fi
      else
        shift
      fi
    ;;
    info)
      report="no"
      export action="info"
      if [ -n "$2" ] ; then
        echo "$2" | egrep -q "^-.$"
        if [ "$?" != "0" ] ; then
          export project=$(shell_filter_strict $2)
          shift 2
        else
          shift
        fi
      else
        shift
      fi
    ;;
    todo)
      report="no"
      export action="todo"
      shift ;;
    -i)
      interactive="yes"
      shift ;;
    -f)
      force="yes"
      shift ;;
    -t)
      testmode="yes"
      runcommand="cat"
      shift ;;
    -o)
      options="$2"
      shift 2;;
    -d)
      debug="$(shell_filter_strict $2)"
      shift 2;;
    -r)
      report="$(shell_filter_strict $2)"
      shift 2;;
    -s)
      show="$(shell_filter_strict $2)"
      shift 2;;
    -c)
      counts="$(shell_filter_strict $2)"
      shift 2;;
    -g)
      greppattern="$(shell_filter_strict $2)"
      # greppattern="$(shell_filter $2)" # This allows partial regexp usage
      # greppattern="$2" # This allows full regexp usage but is highly insecure if you sudo puppi
      shift 2;;
    *)
      showhelp
      exit
      ;;
  esac
done

# Action!
case $action in 
    check) check ;;
    checkhost) check_host ;;
    log) create_runtime_conf ; log ;;
    info) create_runtime_conf ; info ;;
    todo) create_runtime_conf ; todo ;;
    rollback) create_runtime_conf ; rollback ;;
    deploy) create_runtime_conf ; deploy ;;
    init) create_runtime_conf ; initialize ;;
esac
